package org.ielse.frame.view;

import org.ielse.frame.util.BaseUtil;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;

public class ShinyLayout extends FrameLayout {

	private ShinyView viewShiny;
	private int touchSlop;
	private int downX, downY;

	public ShinyLayout(Context context) {
		super(context);
		setBackgroundColor(0xffcccccc);
		touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
		viewShiny = new ShinyView(context);
		addView(viewShiny);
	}

	@Override public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downX = (int) ev.getX();
			downY = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			if (Math.abs(ev.getX() - downX) > touchSlop || Math.abs(ev.getY() - downY) > touchSlop) {
				viewShiny.refreshShiny(ev);
			}
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			viewShiny.refreshShiny(ev);
			break;
		}
		return super.dispatchTouchEvent(ev);
	}

	@Override public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			return true;
		}
		return super.onTouchEvent(event);
	}

	public static class ShinyView extends View {
		class Shiny {
			private int radius, radius2;
			private float fraction;

			public Shiny() {
			}

			public Shiny(int radius, int radius2) {
				this.radius = radius;
				this.radius2 = radius2;
			}

			public int getRadius() {
				return radius;
			}

			public void setRadius(int radius) {
				this.radius = radius;
			}

			public int getRadius2() {
				return radius2;
			}

			public void setRadius2(int radius2) {
				this.radius2 = radius2;
			}

			public void setFraction(float fraction) {
				this.fraction = fraction;
			}
		}

		public class ShinyEvaluator implements TypeEvaluator {
			private Shiny shiny = new Shiny(0, 0);

			@Override public Object evaluate(float fraction, Object startValue, Object endValue) {
				Shiny startPoint = (Shiny) startValue;
				Shiny endPoint = (Shiny) endValue;
				int x = startPoint.getRadius() + (int) (fraction * (endPoint.getRadius() - startPoint.getRadius()));
				shiny.setRadius(x);
				if (fraction >= 0.5f) {
					int y = startPoint.getRadius2() + (int) ((fraction - 0.5) * 2f * (endPoint.getRadius2() - startPoint.getRadius2()));
					shiny.setRadius2(y);
				}
				return shiny;
			}

		}

		private static final int ACTION_NONE = 0;
		private static final int ACTION_MOVE = 1;
		private static final int ACTION_UP_AFTER_MOVE = 2;
		private static final int ACTION_UP = 3;
		private int action = ACTION_NONE;

		private final Paint paint = new Paint();
		private final PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);
		private MotionEvent event;
		private int shinyWidth;
		private int statusBarHeight;
		private ValueAnimator anim1, anim2;

		private final Shiny shiny = new Shiny();

		public ShinyView(Context context) {
			super(context);
			setLayerType(LAYER_TYPE_SOFTWARE, null);
			paint.setAntiAlias(true);
			statusBarHeight = BaseUtil.getStatusBarHeight(context);

			WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
			DisplayMetrics outMetrics = new DisplayMetrics();
			wm.getDefaultDisplay().getMetrics(outMetrics);

			shinyWidth = BaseUtil.isScreenVertical((Activity) context) ? (int) (outMetrics.widthPixels * 0.1f)
					: (int) (outMetrics.heightPixels * 0.1f);

			anim1 = ValueAnimator.ofObject(new ShinyEvaluator(), new Shiny(0, 0), new Shiny(shinyWidth, (int) (shinyWidth * 0.92f)));
			anim1.setDuration(400);
			anim1.setInterpolator(new DecelerateInterpolator(2));
			anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
				@Override public void onAnimationUpdate(ValueAnimator animation) {
					Shiny mShiny = (Shiny) animation.getAnimatedValue();
					shiny.setRadius(mShiny.radius);
					shiny.setRadius2(mShiny.radius2);
					invalidate();
				}
			});
			anim1.addListener(new AnimatorListenerAdapter() {
				@Override public void onAnimationEnd(Animator animation) {
					super.onAnimationEnd(animation);
					action = ACTION_NONE;
					invalidate();
				}
			});

			anim2 = ValueAnimator.ofInt((int) (shinyWidth * 0.7f), shinyWidth);
			anim2.setInterpolator(new DecelerateInterpolator(2));
			anim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
				@Override public void onAnimationUpdate(ValueAnimator animation) {
					int radius = (int) animation.getAnimatedValue();
					float f = animation.getAnimatedFraction();
					shiny.setRadius(radius);
					shiny.setFraction(f);
					invalidate();
				}
			});
		}

		private void refreshShiny(MotionEvent ev) {
			event = ev;
			switch (ev.getAction()) {
			case MotionEvent.ACTION_MOVE:
				action = ACTION_MOVE;
				shiny.radius = (int) (shinyWidth * 0.7f);
				invalidate();
				break;
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				if (action == ACTION_MOVE) {
					action = ACTION_UP_AFTER_MOVE;
					anim2.start();
				} else {
					action = ACTION_UP;
					anim1.start();
				}
				break;
			}
		}

		@Override protected void onDraw(Canvas canvas) {
			if (event != null)
				switch (action) {
				case ACTION_UP:
					paint.setColor(Color.WHITE);
					canvas.drawCircle(event.getX(), event.getY() - statusBarHeight, shiny.radius, paint);
					paint.setXfermode(porterDuffXfermode);
					paint.setColor(Color.TRANSPARENT);
					canvas.drawCircle(event.getX(), event.getY() - statusBarHeight, shiny.radius2, paint);
					paint.setXfermode(null);
					break;
				case ACTION_MOVE:
					paint.setColor(Color.WHITE);
					canvas.drawCircle(event.getX(), event.getY() - statusBarHeight, shiny.radius * 0.7f, paint);
					break;
				case ACTION_UP_AFTER_MOVE:
					paint.setARGB((int) (255 * (1 - shiny.fraction)), 255, 255, 255);
					canvas.drawCircle(event.getX(), event.getY() - statusBarHeight, shiny.radius, paint);
					break;
				}
		}
	}
}
